package com.wlj.security.app;

import com.wlj.security.core.properties.OAuth2ClientProperties;
import com.wlj.security.core.properties.WljSecurityProperties;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.ArrayList;

//认证服务器，继承AuthorizationServerConfigurerAdapter可以对默认token存储等进行配置
@Configuration
@EnableAuthorizationServer
public class AppAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 以下是默认实现的"授权码模式"认证方式
     *  认证服务器有两个重要的地址:/oauth/authorize,/oauth/token
     *  /oauth/authorize  是用户确认是否授权给第三方的地址
     *      参数:response_type='code'   client_id='第三方标识id'    redirect_url='第三方接受code的地址'   scope='all'
     *           跳转过去后还需要输入"用户名和密码"，我们要知道哪个用户授权的，且该用户必须有"ROLE_USER"的角色
     *           client_id默认随机生成，但可自定义配置  security.oauth2.client.client-id:xxxxx
     *
     *  /oauth/token  是换取access_token的地址,跟哪种模式无关
     *      请求头: Content-Type=application/x-www-form-urlencoded    表单提交方式
     *             Authorization=Basic xxxxxxxxx  (Basic Auth输入的用户名和密码是该第三方的client_id,client_secret)
     *      参数:  grant_type='authorization_code'   code='xxx'   client_id='xxx'
     *            redirect_uri='跟上面的一样'   scope='all'
     */

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private WljSecurityProperties wljSecurityProperties;

    /**
     * 客户端配置
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if (ArrayUtils.isNotEmpty(wljSecurityProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties client : wljSecurityProperties.getOauth2().getClients()) {
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds()) //单位:s
                        .refreshTokenValiditySeconds(2592000)  //单位:s
                        .scopes("read","write");
            }
        }
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 客户端认证之前的过滤器
//        security.addTokenEndpointAuthenticationFilter();
        security.tokenKeyAccess("isAuthenticated()")
                .checkTokenAccess("isAuthenticated()");
        security.passwordEncoder(NoOpPasswordEncoder.getInstance()); //clientSecret就无需加密了
    }

    //因为继承了AuthorizationServerConfigurerAdapter，上面两个bean要自己配
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore);
        if(jwtAccessTokenConverter!=null&&jwtTokenEnhancer!=null){
            TokenEnhancerChain chain = new TokenEnhancerChain();
            ArrayList<TokenEnhancer> list = new ArrayList<>();
            list.add(jwtTokenEnhancer);
            list.add(jwtAccessTokenConverter);
            chain.setTokenEnhancers(list);

            endpoints.tokenEnhancer(chain)
                     .accessTokenConverter(jwtAccessTokenConverter);
        }
    }
}
